grammar SCPAR;

options {
    backtrack=true;
    memoize=true;
    output=AST;
}

tokens {
	ARG;
	UNIT;
	VARDEF;
	PARAM;
	CALL;
	VECTOR;
	VARS;
	PARAMS;
	NAMES;
	TYPES;
	BLOCKS;
	FUNCS;
	INITIALIZE;
}

scope Symbols {
	Set types; // only track types in order to get parser working
}

@header {
package org.cpar.compiler;

import java.util.Set;
import java.util.HashSet;
import org.cpar.compiler.nodes.*;
}

@lexer::header{
package org.cpar.compiler;	
}

@members {
	boolean isTypeName(String name) {
		System.out.print(name);
		for (int i = Symbols_stack.size()-1; i>=0; i--) {
			Symbols_scope scope = (Symbols_scope)Symbols_stack.get(i);
			if ( scope.types.contains(name) ) {
				return true;
			}
		}
		return false;
	}
}

translation_unit
scope Symbols; // entire file is a scope
@init {
	$Symbols::types = new HashSet();
}
	:	external_declaration+ -> ^(UNIT<Unit> external_declaration+)
	;

external_declaration
	:	var_declaration
	| task_declaration
	| c_function
	| main_function
	|	monitor_block
	| enum_declaration
	;
	
monitor_block
	:	MONITOR IDENTIFIER var_declaration* '{' c_function+ ('{' statements '}')? '}' -> ^(MONITOR<MonitorBlock> IDENTIFIER ^(VARS var_declaration*) ^(FUNCS c_function+) ^(INITIALIZE statements?) ) 
	;
	
c_function
	:	type_declaration IDENTIFIER '(' parameter_declarations? ')' (';' | '{' function_body '}') -> ^(IDENTIFIER<AnsiFunction> ^('(' parameter_declarations? ')') ^(BODY function_body?) ^(RETURN type_declaration) )
	;
	
main_function
	:	VOID? MAIN '(' ')' '{' function_body '}' -> ^(MAIN<MainFunction> ')' function_body '}' )
	;
	
task_declaration
	:	task_spec
	| task_body
	;
	
task_spec
	:	TASK SPEC IDENTIFIER '(' parameter_declarations? ')' ';' -> ^(TASK<TaskSpec> ^(IDENTIFIER parameter_declarations? ) '(')
	;
	
task_body
	:	TASK BODY IDENTIFIER '(' parameter_declarations? ')' '{' function_body '}' -> ^(TASK<TaskBody> ^(IDENTIFIER parameter_declarations?) '(' ^(BODY function_body) '{')
	;

parameter_declarations
	:	parameter_declaration  (',' parameter_declaration)* -> ^(PARAMS parameter_declaration+)
	;
	
	
parameter_declaration
	:	c_var_declaration -> ^(PARAM c_var_declaration)
	;
	
function_body
	:	var_declaration* statements? -> ^(VARS<VarDeclarations> var_declaration*) ^(BODY statements?)
	;
	
statements
	:	statement+
	;
	
statement
	:	function_call -> ^(CALL function_call)
	| parallel_block
	| assignment_expression 
	| if_statement
	| for_statement
	| forall_statement
	| '{' statements '}'
	| return_statement
	| loop_statement
	| ';'
	;
	
loop_statement
	: DO statement WHILE '(' expression ')'
	|	WHILE '(' assignment_expression ')' statement	
	;	
	
return_statement
	: RETURN expression?	
	;	

parallel_block
	:	COBEGIN statements (ALSO statements)+ COEND -> ^(COBEGIN<ParallelBlock> ^(BLOCKS ^(ALSO statements)+) COEND)
	| PARBEGIN statements (ALSO statements)+ PAREND -> ^(PARBEGIN<MicroBlock> ^(BLOCKS ^(ALSO statements)+) PAREND)
	;
	
forall_statement
	:	FORALL assignment_expression TO expression statement -> ^(FORALL<ForAllStatement> assignment_expression expression ^(BODY statement))
	;
	
if_statement
	:	IF '(' expression ')' statement (ELSE statement)?
	;
	
for_statement
	:	FOR '(' expression? ';' expression? ';' expression? ')' statement
	;
	
	
function_call
	:	c_function_call
	| cpar_function_call
	;
	
cpar_function_call
	:	semaphore_function_call
	| event_function_call
	| proc_function_call
	;
	
proc_function_call
	:	ALLOC_PROC '(' constant ')' -> ^(ALLOC_PROC<AllocProcCall> constant)
	| CREATE assignment_expression ',' function_call -> ^(CREATE<CreateProcCall> assignment_expression function_call)
	| WAIT_ALL '(' ')' -> ^(WAIT_ALL<WaitAllCall> ')')
	;
	

event_function_call
	:	event_type '(' '&' unary_expression ')' -> ^(event_type unary_expression)
	;
	
event_type
	:	CREATE_EV -> CREATE_EV<EventStatement>
	|	SET_EV -> SET_EV<EventStatement>
	|	REM_EV -> REM_EV<EventStatement>
	|	RES_EV -> RES_EV<EventStatement>
	|	WAIT_EV -> WAIT_EV<EventStatement>
	|	READ_EV -> READ_EV<EventStatement>
	;
	
semaphore_function_call
	:	semaphore_type '(' '&' IDENTIFIER (',' unary_expression)? ')' -> ^(semaphore_type IDENTIFIER unary_expression?)
	;
	
semaphore_type
	:	CREATE_SEM<SemaphoreStatement>
	|	REMOVE_SEM<SemaphoreStatement>
	|	LOCK<SemaphoreStatement>
	|	UNLOCK<SemaphoreStatement>
	;

c_function_call
	:	IDENTIFIER '(' argument_list? ')' -> ^(IDENTIFIER '(' argument_list? ')')
	;
	
argument_list
	:	argument (',' argument)* -> argument+
	;
	
argument
	:	expression -> ^(ARG expression)
	;
	
expression
	: assignment_expression (',' assignment_expression)* -> assignment_expression+
	;

assignment_expression
	: lvalue assignment_operator assignment_expression -> ^(assignment_operator lvalue assignment_expression)
	| conditional_expression
	;

conditional_expression
	: binary_expression ('?' expression ':' conditional_expression)? -> binary_expression (expression conditional_expression)?
	;	

binary_expression
	:	cast_expression (operator cast_expression)* -> cast_expression+
	;

operator
	:	PLUS
	|	MINUS
	|	'*'
	|	'/'
	|	'%'
	|	LEFT_SHIFT
	|	RIGHT_SHIFT
	|	LOGICAL_OR
	|	LOGICAL_AND
	|	BITWISE_OR
	|	BITWISE_XOR
	|	EQUALS
	|	NOT_EQUALS
	|	LESS_THAN 
	|	GREATER_THAN 
	| LESS_EQUALS 
	| GREATER_EQUALS
	;

cast_expression
	: '(' (IDENTIFIER|'*')+ ')' cast_expression -> cast_expression
	| unary_expression
	;

assignment_operator
	: ASGN_EQUAL -> ASGN_EQUAL
	| ASGN_TIMES -> ASGN_TIMES
	| ASGN_DIVIDE -> ASGN_DIVIDE
	| ASGN_MOD -> ASGN_MOD
	| ASGN_PLUS -> ASGN_PLUS
	| ASGN_MINUS -> ASGN_MINUS
	| ASGN_LSHIFT -> ASGN_LSHIFT
	| ASGN_RSHIFT -> ASGN_RSHIFT
	| ASGN_BITWISE_AND -> ASGN_BITWISE_AND
	| ASGN_BITWISE_XOR -> ASGN_BITWISE_XOR
	| ASGN_BITWISE_OR -> ASGN_BITWISE_OR
	;
	
	
lvalue
	:	unary_expression
	;	
	
constant_expression
	:	constant
	;
	
constant
  : HEX_LITERAL -> HEX_LITERAL
  | OCTAL_LITERAL -> OCTAL_LITERAL
  | DECIMAL_LITERAL -> DECIMAL_LITERAL
  |	CHARACTER_LITERAL -> CHARACTER_LITERAL
	|	STRING_LITERAL -> STRING_LITERAL
  | FLOATING_POINT_LITERAL -> FLOATING_POINT_LITERAL
  ;
    
primary_expression
options{		k=2;	}
	:	constant
	| '(' expression ')'
	| function_call
	| IDENTIFIER '.' c_function_call -> ^(IDENTIFIER<MonitorCall> c_function_call )
	| IDENTIFIER -> IDENTIFIER<VarReference>
	;
	

unary_expression
	:	('++'|'--') unary_expression
	|	unary_operator cast_expression
	| primary_expression postfix_expression*
	;
	
postfix_expression
	:	'[' expression ']'
	|	'++'
	| '--'
	;
	
unary_operator
	:	ADDRESSOF
	| INDIRECTION
	| PLUS
	| MINUS
	| BITWISE_NOT
	| NOT
	;
	
declaration_sufix
	:	'[' constant ']' -> ^(VECTOR constant)
	;
	
var_declaration
	:	c_var_declaration declaration_sufix* ';' -> ^(VARDEF ^(SHARED<SharedObject> c_var_declaration declaration_sufix*))
	|	shared_var_declaration ';' -> ^(VARDEF shared_var_declaration ';')
	;
	
	
c_var_declaration
	:	type_declaration var_names// -> (type_declaration )
	;
	
shared_var_declaration
	:	SHARED type_declaration var_names declaration_sufix* -> ^(SHARED<SharedObject> type_declaration var_names)
	;
	
var_names
	:	IDENTIFIER (',' IDENTIFIER)* -> ^(NAMES IDENTIFIER+)
	;
	
type_declaration
	:	type+ '*'* -> ^(TYPES type+ '*'*)
	| struct_declaration
	;
	
enum_declaration
options {k=3;}
	: ENUM '{' enumerator_list '}'
	| ENUM IDENTIFIER '{' enumerator_list '}'
 	| ENUM IDENTIFIER	
	;	
	
enumerator_list
	: enumerator (',' enumerator)*
	;

enumerator
	: IDENTIFIER ('=' constant_expression)? -> ^(IDENTIFIER constant_expression?)
	;	
	
struct_declaration
options {k=3;}
	:	struct_or_union IDENTIFIER? '{' struct_member_list '}'
	| struct_or_union IDENTIFIER
	;
	
struct_member_list
	:	struct_member+
	;

struct_member
	:	type_declaration IDENTIFIER ';'	
	;
	
struct_or_union
	:	STRUCT
	| UNION
	;
	
type
	:	type_modifier* generic_type
	|	cpar_type
	;
	
type_modifier
	:	CONST
	|	VOLATILE
	| EXTERN
	| STATIC
	| AUTO
	| REGISTER
	| CDECL
	;
	
generic_type
	:	VOID
	| CHAR
	| SHORT
	| INT
	| LONG
	| FLOAT
	| DOUBLE
	| SIGNED
	| UNSIGNED
	| W64
	| INT64
	| type_id
	;
	
type_id
    :   {isTypeName(input.LT(1).getText())}? IDENTIFIER -> IDENTIFIER
    ;	
	
cpar_type
	:	SEMAPH -> SEMAPH<CPARType>
	|	EVENT -> EVENT<CPARType>
	;

ALLOC_PROC
	:	'alloc_proc'
	;
	
WAIT_ALL
	:	'wait_all'
	;
	
MAIN
	:	'main'
	;
	
BLOCK
	:	'block'
	;

CREATE_EV
	:	'create_ev'
	;
	
SET_EV
	:	'set_ev'
	;

REM_EV
	:	'rem_ev'
	;
	
RES_EV
	:	'res_ev'
	;
	
WAIT_EV
	:	'wait_ev'
	;
	
READ_EV
	:	'read_ev'
	;
	
	
CREATE_SEM
	:	'create_sem'
	;

REMOVE_SEM
	:	'rem_sem'
	;
	
LOCK:	'lock'
	;
	
UNLOCK
	:	'unlock'
	;	

CDECL
	:	'__cdecl'
	;
	
VOID:	'void';

CHAR:	'char';

SHORT
	:	'short';
	
INT	:	'int';

LONG:	'long';

FLOAT
	:	'float';
	
DOUBLE
	:	'double';

SIGNED
	:	'signed';

UNSIGNED
	:	'unsigned';

SPEC:	'spec';

BODY:	'body';

SEMAPH
	:	'Semaph';

EVENT
	:	'Event'
	;
	
STRUCT
	:	'struct'
	;
	
UNION
	:	'union'
	;

ENUM
	:	'enum'
	;

W64	:	'__w64';

INT64
	:	'__int64';

EXTERN
	:	'extern';

TYPEDEF
	:	'typedef'
	;

CONST
	:	'const';
	
VOLATILE
	:	'volatile';


TASK:	'task';

SHARED
	:	'shared'	;
	
REGISTER
	:	'register'	;
	
AUTO:	'auto';
	
STATIC
	:	'static';

LOGICAL_OR
	:	'||'
	;

LOGICAL_AND
	:	'&&'
	;

LESS_THAN
	:	'<'
	;
	
GREATER_THAN
	:	'>'
	;
	
LESS_EQUALS
	:	'<='
	;
	
GREATER_EQUALS
	:	'>='
	;

LEFT_SHIFT
	:	'<<'
	;
	
RIGHT_SHIFT
	:	'>>'
	;

EQUALS
	:	'=='
	;

NOT_EQUALS
	:	'!='
	;
	
BITWISE_OR
	:	'|'
	;

BITWISE_XOR
	:	'^'
	;

ADDRESSOF
	:	'&'
	;
	
INDIRECTION
	:	'*'
	;
	
PLUS:	'+'
	;
	
MINUS
	:	'-'
	;
	
BITWISE_NOT
	:	'~'
	;
	
NOT	:	'!'
	;

ASGN_EQUAL
	:	'='
	;
	
ASGN_TIMES
	:	'*='
	;

ASGN_DIVIDE
	:	'/='
	;
	
ASGN_MOD
	:	'%='
	;
	
ASGN_PLUS
	:	'+='
	;
	
ASGN_MINUS
	:	'-='
	;
	
ASGN_LSHIFT
	:	'<<='
	;
	
ASGN_RSHIFT
	:	'>>='
	;
	
ASGN_BITWISE_AND
	:	'&='
	;
	
ASGN_BITWISE_XOR
	:	'^='
	;

ASGN_BITWISE_OR
	:	'|='
	;

MONITOR
	:	'monitor'
	;

CASE:	'case'
	;
	
DEFAULT
	:	'default'
	;
	
CREATE
	:	'create'
	;
	
PARBEGIN
	:	'parbegin'
	;

ALSO:	'also'
	;
	
PAREND
	:	'parend'
	;
	
COBEGIN
	:	'cobegin'
	;

COEND
	:	'coend'
	;

IF	:	'if'
	;
	
ELSE
	:	'else'
	;
	
WHEN:	'when'
	;
	
SWITCH: 'switch'
	;
	
WHILE: 'while'
	;
	
DO	:	'do'
	;
	
FOR	:	'for'
	;

TO:	'to'
	;
	
FORALL
	:	'forall'
	;
	
GOTO:	'goto'
	;
	
CONTINUE: 'continue'
	;
	
BREAK
	:	'break'
	;
	
RETURN
	:	'return'
	;
	

IDENTIFIER
	:	LETTER (LETTER|'0'..'9')*
	;
	
fragment
LETTER
	:	'$'
	|	'A'..'Z'
	|	'a'..'z'
	|	'_'
	;

CHARACTER_LITERAL
    :   '\'' ( EscapeSequence | ~('\''|'\\') ) '\''
    ;

STRING_LITERAL
    :  '"' ( EscapeSequence | ~('\\'|'"') )* '"'
    ;

HEX_LITERAL : '0' ('x'|'X') HexDigit+ IntegerTypeSuffix? ;

DECIMAL_LITERAL : ('0' | '1'..'9' '0'..'9'*) IntegerTypeSuffix? ;

OCTAL_LITERAL : '0' ('0'..'7')+ IntegerTypeSuffix? ;

fragment
HexDigit : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
IntegerTypeSuffix
	:	('u'|'U')? ('l'|'L')
	|	('u'|'U')  ('l'|'L')?
	;

FLOATING_POINT_LITERAL
    :   ('0'..'9')+ '.' ('0'..'9')* Exponent? FloatTypeSuffix?
    |   '.' ('0'..'9')+ Exponent? FloatTypeSuffix?
    |   ('0'..'9')+ Exponent FloatTypeSuffix?
    |   ('0'..'9')+ Exponent? FloatTypeSuffix
	;

fragment
Exponent : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;

fragment
FloatTypeSuffix : ('f'|'F'|'d'|'D') ;

fragment
EscapeSequence
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   OctalEscape
    ;

fragment
OctalEscape
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UnicodeEscape
    :   '\\' 'u' HexDigit HexDigit HexDigit HexDigit
    ;

WS  :  (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;}
    ;

COMMENT
    :   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;

LINE_COMMENT
    : '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    ;

// ignore #line info for now
LINE_COMMAND 
    : '#' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    ;
